モデル編集時の逐次検証

概要

Next Design エクステンションでは、Next Design でのモデル操作やファイル操作などをイベントとして受け取り、そのイベントごとに独自の処理を加える機能を実現できます。

以下では、前のチュートリアル: モデルの一括検証 で作成したエクステンションをベースに、モデル編集操作に合わせてモデルを逐次検証するように機能拡張してみましょう。

  • 全体の流れ

    • マニフェストによるイベントの拡張ポイント定義
    • イベントハンドラによる検証処理の呼び出し

ゴールイメージ

画面キャプチャ or GIFアニメ

  • Next Design のサンプルプロジェクトから [先進運転システムソフト開発] プロジェクトを開きます。

  • いずれかのモデルを選択し、その名前を変更して確定すると、逐次、次の検証ルールに従ってそのモデルが検証され、エラーがあれば表示されます。

    検証ルール: モデル名に半角スペース文字が含まていないこと

ダウンロード

  • 次のリンクから本チュートリアルの結果として作成されるファイル一式をダウンロードできます。

    ダウンロードリンク: ValidationSample-2.zip

マニフェストによるイベントの拡張ポイント定義

モデルの名前はモデルが持つフィールドの一種です。名前の変更はモデルのフィールド変更イベントとして受け取れます。

モデルのフィールド変更イベントを受け取り、イベントハンドラを呼び出すために、マニフェストに次の内容を定義します。

イベントの拡張ポイント定義

{
  "extensionPoints": {
    "events": {
      // イベントの拡張ポイント定義
    }
  }
}

実装コード

manifest.json

{
  // エクステンション定義
  "name": "Validation Sample",
  "version": "1.1.0",
  "publisher": "DENSO CREATE INC.",
  "license": "Next Design 使用許諾契約書に準ずる。Copyright (C) 2019 DENSO CREATE INC. All rights reserved.",

  "main": "ValidationSample.dll",         // エントリーポイントとしてビルド結果の DLL ファイル名を指定します。
  "lifecycle": "project",                 // ライフサイクルとしてプロジェクトライフサイクルを指定します。
  "baseprofile": "車載システムソフト開発",  // 対象プロジェクトの条件としてプロファイル名を指定します。

  // 拡張ポイント定義
  "extensionPoints": {
    // リボン
    "ribbon": {
      "tabs": [
        // エクステンション用に追加するリボンタブを定義します。
        {
          "id": "MyExtensions.MainTab",
          "label": "My Extensions",
          "orderAfter": "System.Help",
          "groups": [
            // リボンタブ内を区切るグループを定義します。
            {
              "id": "MyExtensions.Validation.Group",
              "label": "モデル検証",
              "controls": [
                // 検証実行ボタンを定義します。
                {
                  "type": "Button",
                  "id": "MyExtensions.Validation.RunButton",
                  "label": "検証",
                  "description": "全モデルを検証します。",
                  "imageLarge": "resources/icon.png",
                  "command": "Command.Validation.Run"   // 後述のコマンドに定義される検証コマンドの id を指定します。
                }
              ]
            }
          ]
        }
      ]
    },

    // コマンド
    "commands": [
      // 検証処理のコマンドハンドラ `Run` を呼び出す検証コマンドを定義します。
      {
        "id": "Command.Validation.Run",
        "execFunc": "Run",            // エントリーポイントのメインクラスに実装される公開メソッドを指定します。
        "canExecWhen": {
          "uiState": "ProjectOpened"  // コマンドの有効条件としてプロジェクトが開いていることを指定します。
        }
      }
    ],

    // イベント
    "events": {
      // モデルに関するイベントの拡張ポイントを追加します。
      "models": [
        {
          "class": "*",   // 全モデル共通で検証処理を行う場合は、イベントフィルタに "*" を指定、もしくは、この行を省略します。
          // 特定クラスだけを対象に検証処理を行う場合は、イベントフィルタに対象クラス名をカンマ区切りで列挙します。
          // "class": "TechnicalArchitectureComponentECU,TechnicalArchitectureComponentSensor",
          "onFieldChanged": "OnFieldChanged"  // モデルのフィールド変更時のイベントハンドラとして、エントリーポイントのメインクラスに実装される公開メソッドを登録します。
        }
      ]
    }
  }
}

イベントハンドラによる名前検証処理の呼び出し

イベントから呼び出されたイベントハンドラで、イベントの詳細を確認して名前検証処理を呼び出すには、次の内容を公開メソッドとして実装します。

モデルのフィールド変更のイベントハンドラ

public class ValidationSample : IExtension
{
    // モデルのフィールド変更のイベントハンドラ
    public void OnFieldChanged(IEventContext context, IEventParams eventParams)
    {
      // イベントハンドラの処理
    }
}

実装コード

ValidationSample.cs

// エクステンション開発で参照するAPIの名前空間の宣言
using NextDesign.Extension;
using NextDesign.Core;
using NextDesign.Desktop;

/// <summary>
/// エントリーポイントとなるDLLのメインクラス
/// </summary>
/// <remarks>
/// エントリーポイントのメインクラスには、 `IExtension` インタフェースを実装します。
/// ハンドラはこのクラスの公開メソッドとして実装します。
/// </remarks>
public class ValidationSample : IExtension
{
    /// <summary>
    /// 検証処理のコマンドハンドラ
    /// </summary>
    /// <param name="context">コマンドコンテキスト</param>
    /// <param name="parameters">コマンドパラメータ</param>
    /// <remarks>
    /// 検証処理のコマンドハンドラをメインクラスの公開メソッドとして実装します。
    /// </remarks>
    public void Run(ICommandContext context, ICommandParams parameters)
    {
        var app = context.App;
        var project = app.Workspace.CurrentProject;

        // 以前のエラーをすべてクリアします。
        app.Errors.ClearErrors();

        // エラー一覧ウィンドウを表示します。
        app.Window.IsInformationPaneVisible = true;
        app.Window.ActiveInfoWindow = "Error";

        // プロジェクト中のすべてのモデルを対象に繰り返し処理を行います。
        var models = project.GetAllChildren();
        foreach (var model in models)
        {
            // 検証ルールに従ってモデルを検証します。
            ValidateModel(model);
        }
    }

    /// <summary>
    /// モデルのフィールド変更のイベントハンドラ
    /// </summary>
    /// <param name="context">イベントコンテキスト</param>
    /// <param name="eventParams">イベントパラメータ</param>
    /// <remarks>
    /// モデルのフィールド変更イベントハンドラをメインクラスの公開メソッドとして実装します。
    /// </remarks>
    public void OnFieldChanged(IEventContext context, IEventParams eventParams)
    {
        // フィールド変更されたモデルやフィール名などの詳細情報は、イベントハンドラの第2引数に渡されるイベントパラメータの中から取得します。
        // イベントパラメータの具体的な型はイベントの種類に応じて異なるため、モデルのフィールド変更イベント用のイベントパラメータに型変換します。
        var fieldChangeParams = eventParams as ModelFieldChangedEventParams;
        var model = fieldChangeParams.Model;        // フィールド変更されたモデル
        var fieldName = fieldChangeParams.Field;    // フィールド変更されたフィールド名
        var app = context.App;

        // 変更されたフィールドが `Name` フィールドの場合のみ検証します。
        if (fieldName == "Name")
        {
            // 対象モデルに対する以前のエラーをクリアします。
            app.Errors.ClearErrorsAt(model);

            // 検証ルールに従ってモデルを検証します。
            ValidateModel(model);
        }
    }

    /// <summary>
    /// 検証ルールに従ってモデルを検証
    /// </summary>
    /// <param name="model">モデル</param>
    private void ValidateModel(IModel model)
    {
        // 次の検証ルールと照合します。
        // ・モデル名には半角スペースを含めないこと
        if (model.Name.IndexOf(" ") > 0)
        {
            // 検証ルールに合わない場合は、該当モデルにエラー情報を追加します。
            var message = string.Format("モデル名に半角スペースが含まれています。モデル名: {0}", model.Name);
            var error = model.AddError("Name", "Error", "モデル命名規則チェック", message);
        }
    }

    /// <summary>
    /// エクステンションの初期化処理
    /// </summary>
    /// <param name="context">実行コンテキスト</param>
    /// <remarks>
    /// エクステンションの初期化処理・終了処理が不要な場合も、空の `Activate`, `Deactivate` メソッドが必要です。
    /// </remarks>
    public void Activate(IContext context)
    {
        // 必要に応じてエクステンションの初期化処理などを実装します。
    }

    /// <summary>
    /// エクステンションの終了処理
    /// </summary>
    /// <param name="context">実行コンテキスト</param>
    /// <remarks>
    /// エクステンションの初期化処理・終了処理が不要な場合も、空の `Activate`, `Deactivate` メソッドが必要です。
    /// </remarks>
    public void Deactivate(IContext context)
    {
        // 必要に応じてエクステンションの終了処理などを実装します。
    }
}